home *** CD-ROM | disk | FTP | other *** search
- /****
- File.....: FS_Memory.c
- Date.....: Wednesday, April 10, 1996
- By.......: Ken Earle. No rights reserved.
- Status...: tested
- Spec.....: GrabUpperSystemMemory(): allocate memory in the system heap, leaving
- enough room for the "real" application.
- ReleaseUpperSystemMemory(): release same memory.
- Revisions:
- ****/
-
- #include "FakeStart.h"
- #include "FS_Memory.h"
-
- // Define this to debug GrabUpperSystemMemory()
- //#define Debug_Memory
-
-
- // Memory sizes, using the Finder's units of 1024-byte chunks
- #define MINIMUM_FOR_APP (2700*1024L) // default size of app
- #define ONE_MEG (1024*1024L)
- #define ABOUT_THIRTEEN_MEG ( (13*1024*1024L) + (250*1024L) )
- #define SIXTEEN_MEG (16*1024*1024L)
-
- // CHANGE this to determine how high in memory your application can go.
- // Note this is the limit for the top of your app's heap, not the bottom.
- #define APPLICATION_LIMIT ABOUT_THIRTEEN_MEG
-
- #define MINIMUM_FREE_INC (100*1024L) // minimum increment for free heap
- #define MINIMUM_FREE (MINIMUM_FREE_INC * 3) // initial minimum
-
- // Memory error codes, for debugging only
- typedef enum DBMemoryError
- {
- eAppLeftTooHigh_DBMem = 1,
- eTooManyFragments_DBMEM = 2,
- eUnknown_DBMem = 3,
- eReleaseLowestFailed_DBMem = 4,
- eCouldNotGetLastHandle_DBMem = 5,
- eCouldNotGetAppChunk_DBMem = 6,
- eCouldNotLeaveJustApp_DBMem = 7,
- eCouldNotFindFreeHandle_DBMem = 8
- } DBMemoryError;
-
-
- // Functions used here only
- // Memory-related support functions
- static OSErr AllocateSysMemUntilBelowLimit
- (Handle *bigChunkHA, // array of Handle
- short topNumHandles, // maximum Handles in array
- long leaveThisMuch, // for application, or 0, plus...
- Size *safetyMarginP // <->...a few 100K, set as we go
- );
- static OSErr ReleaseLowestHandle
- (Handle *bigChunkHA, // array of Handle
- short topNumHandles, // maximum Handles in array
- long leaveThisMuch, // for application, or 0, plus...
- Size safetyMargin, // ->...a few 100K, set as we go
- short *lowestWhichHP // <- which handle was released
- );
- static OSErr VerifyThereIsRoomForApp
- (Handle *bigChunkHA, // array of Handle
- long leaveThisMuch, // for application, or 0, plus...
- short lowestWhichH, // index of lowest bigChunkHA[]
- Size *safetyMarginP // <->...a few 100K, set as we go
- );
- static OSErr AllocateContiguousChunk
- (Size availableContigSysMem, // biggest single piece from sys
- long leaveThisMuch, // for application, or 0, plus...
- Size *safetyMarginP, // <->...a few 100K, set as we go
- Handle *tempMemHP // <- address of Handle
- );
- // Resource-related support
- static OSErr PreferredAndMinimumForApplication
- (FSSpecPtr appSpecP, // ->for the application
- long *preferredSizeP, // <-from SIZE rsrc
- long *minimumSizeP // <-ditto
- );
-
-
- /* Grab system memory up high, force application to be loaded low. Verify that real
- app can be loaded low. Error here implies that memory is fragmented, or not enough
- memory is currently free to allow running the app. The codes in DBMemoryError are for
- debugging, only a generic "out of memory" error is returned from here.
-
- A general algorithm (especially good if memory is fragmented):
- 1-allocate until contiguous system memory < leaveThisMuch
- 2-release lowest Handle (if any)
- 3-allocate all but room for app out of the lowest contiguous chunk
- 4-allocate for app, verify it is low, release its Handle
- 5-return with all memory locked up except low spot for the app.
-
- Note temp mem is allocated high by TempNewHandle() within a particular chunk. There
- is often difficulty allocating the very last (lowest) handle: to get around this, a
- "safetyMargin" (300-400k) is used to leave a little bit free at the heap bottom.
- Regular systems require about 2-300K, those using RamDoubler seem to need more like
- 400K. If there's a problem with the last handle, we increase the amount of slop
- (safetyMargin) and try repeatedly to clear the error.
- */
- OSErr GrabUpperSystemMemory
- (FSSpecPtr appSpecP, // ->"real" application, for SIZE rsrc
- Handle *bigChunkHA, // <-array of Handle
- short topNumHandles // maximum Handles in array
- )
- {
- Size availableContigSysMem; // biggest single piece from sys
- Size grow; // value not used (always 0)
- Size safetyMargin; // a few 100K, varies per system
- long preferredSize; // from SIZE for real app
- long minimumSize; // ditto
- long leaveThisMuch; // preferred size (or?)
- short lowestWhichH = -1; // index of lowest bigChunkHA[]
- OSErr theErr = noErr;
-
- // Consult SIZE rsrc for app to determine memory needed.
- // Decide on amount to leave for real app, "leaveThisMuch".
- theErr = PreferredAndMinimumForApplication( appSpecP, &preferredSize,
- &minimumSize );
- if (theErr != noErr)
- {
- // If no SIZE, use some defaults and attempt to continue.
- theErr = noErr;
- preferredSize = MINIMUM_FOR_APP;
- minimumSize = MINIMUM_FOR_APP;
- }
-
- // We ignore the minimumSize, but it's there if you need it....
- leaveThisMuch = preferredSize;
-
- // Is there room at all?
- availableContigSysMem = TempMaxMem(&grow);
- if (availableContigSysMem < leaveThisMuch)
- theErr = memFullErr;
-
- // Start with a reasonably small safety margin.
- safetyMargin = MINIMUM_FREE;
-
- // 1-allocate until contiguous system memory < leaveThisMuch
- if (theErr == noErr)
- {
- theErr = AllocateSysMemUntilBelowLimit( bigChunkHA, topNumHandles,
- leaveThisMuch, &safetyMargin );
- }
-
- // If allocation just above failed on last handle, try to proceed anyway.
- if (theErr == eCouldNotGetLastHandle_DBMem)
- theErr = noErr;
-
- // 2-release lowest handle (if any and not enough left free).
- if (theErr == noErr)
- {
- theErr = ReleaseLowestHandle( bigChunkHA, topNumHandles, leaveThisMuch,
- safetyMargin, &lowestWhichH );
- }
-
- // 3-allocate all but room for app-plus-safety out of the lowest contiguous chunk.
- if (theErr == noErr)
- {
- availableContigSysMem = TempMaxMem(&grow);
- theErr = AllocateContiguousChunk( availableContigSysMem, leaveThisMuch,
- &safetyMargin, &bigChunkHA[lowestWhichH] );
- if (theErr != noErr)
- theErr = eCouldNotLeaveJustApp_DBMem;
- }
-
- // 4-allocate for app, verify it is low, release its Handle.
- if (theErr == noErr)
- {
- theErr = VerifyThereIsRoomForApp( bigChunkHA, leaveThisMuch,
- lowestWhichH, &safetyMargin );
- }
-
- // Clean up if there was an error.
- if (theErr != noErr)
- ReleaseUpperSystemMemory(bigChunkHA, topNumHandles);
-
- #ifdef Debug_Memory
- // Debug only, sound a few beeps (see DBMemoryError above).
- if (theErr > 0 && theErr < 10)
- {
- short i;
-
- for (i = 1; i <= theErr; ++i)
- SysBeep(2);
- }
- #endif
-
- // Return general error
- if (theErr != noErr)
- theErr = memFullErr;
- // 5-return with all memory locked up except low spot for the app.
- return theErr;
- }
-
- /* Release temporary memory. Not really needed, just nice to do.
- Otherwise, system memory would be released automatically when this app quits.
- */
- void ReleaseUpperSystemMemory
- (Handle *bigChunkHA, // <->array of Handle
- short topNumHandles // maximum Handles in array
- )
- {
- short i;
-
- // Deallocate system memory.
- for (i = 0; i < topNumHandles; ++i)
- {
- if (bigChunkHA[i] != NULL)
- {
- DisposeHandle(bigChunkHA[i]);
- bigChunkHA[i] = NULL;
- }
- }
- }
-
-
- // Functions used here only
-
- // Memory-related support functions
-
- /* Allocate handles out of system memory to bigChunkHA[] until contig memory left is
- less than leaveThisMuch. Start at index 0 in bigChunkHA[]. Returns error if array
- overflow or cannot allocate handle. May set *safetyMarginP to a larger value at end.
- */
- static OSErr AllocateSysMemUntilBelowLimit
- (Handle *bigChunkHA, // array of Handle
- short topNumHandles, // maximum Handles in array
- long leaveThisMuch, // for application, or 0, plus...
- Size *safetyMarginP // <->...a few 100K, set as we go
- )
- {
- Size availableContigSysMem; // biggest single piece from sys
- Size grow; // value not used (always 0)
- Size safetyMargin; // a few 100K, varies per system
- short whichH = 0; // into bigChunkHA[]
- OSErr theErr = noErr;
-
- // Determine amount of sys mem available, get the safety margin.
- availableContigSysMem = TempMaxMem(&grow);
- safetyMargin = *safetyMarginP;
-
- // Allocate until contig < leaveThisMuch.
- while ( availableContigSysMem >= leaveThisMuch
- && theErr == noErr )
- {
- // Avoid array overflow
- if (whichH >= topNumHandles)
- {
- theErr = eTooManyFragments_DBMEM;
- break;
- }
- // Allocate the chunk, leaving safety margin free.
- theErr = AllocateContiguousChunk( availableContigSysMem, 0,
- &safetyMargin, &bigChunkHA[whichH] );
- if (theErr == noErr)
- {
- // Move to next handle, see if there's another free chunk.
- ++whichH;
- availableContigSysMem = TempMaxMem(&grow);
- }
- else
- {
- // Will attempt to proceed (may have been a small handle at end).
- bigChunkHA[whichH] = NULL;
- theErr = eCouldNotGetLastHandle_DBMem;
- }
- }
- *safetyMarginP = safetyMargin;
- return theErr;
- }
-
- /* If there is not enough system memory free for the application, release
- the lowest handle allocated in bigChunkA[] that is large enough for the app.
- Set *lowestWhichHP to the index for that handle or to a free handle in
- bigChunkA[] if no handle is freed.
- Error if no handle big enough, or if can't find a free handle.
- */
- static OSErr ReleaseLowestHandle
- (Handle *bigChunkHA, // array of Handle
- short topNumHandles, // maximum Handles in array
- long leaveThisMuch, // for application, or 0, plus...
- Size safetyMargin, // ->...a few 100K, set as we go
- short *lowestWhichHP // <- which handle was released
- )
- {
- Size availableContigSysMem; // biggest single piece from sys
- Size grow; // value not used (always 0)
- short lowestWhichH = -1; // index of lowest bigChunkHA[]
- long lowestAddress; // the chunk holding real app
- short i;
- OSErr theErr = noErr;
-
- // How much memory is left?
- availableContigSysMem = TempMaxMem(&grow);
-
- // Release lowest handle, if any and not enough memory left free.
- if (availableContigSysMem < leaveThisMuch + safetyMargin)
- {
- // Find lowest handle, check it is big enough for app plus safety margin.
- lowestAddress = 0;
- for (i = 0; i < topNumHandles; ++i)
- {
- if (bigChunkHA[i] != NULL)
- {
- if ( (lowestAddress == 0
- || (long)*(bigChunkHA[i]) < lowestAddress)
- && GetHandleSize(bigChunkHA[i]) >= leaveThisMuch + safetyMargin )
- {
- lowestAddress = (long)*(bigChunkHA[i]);
- lowestWhichH = i;
- }
- }
- }
- // Release lowest handle.
- if (lowestWhichH >= 0)
- {
- DisposeHandle(bigChunkHA[lowestWhichH]);
- bigChunkHA[lowestWhichH] = NULL;
- }
- else
- {
- // Serious error, no chunk big enough for application.
- theErr = eReleaseLowestFailed_DBMem;
- }
- }
-
- // If we didn't free a handle, adopt an unused one.
- if ( theErr == noErr
- && lowestWhichH < 0 )
- {
- for (i = 0; i < topNumHandles; ++i)
- {
- if (bigChunkHA[i] == NULL)
- {
- lowestWhichH = i;
- break;
- }
- }
- // Bizarre error, shouldn't happen since we have lots of handles.
- if (lowestWhichH < 0)
- theErr = eCouldNotFindFreeHandle_DBMem;
- }
-
- // Set index of handle freed, return error code
- *lowestWhichHP = lowestWhichH;
- return theErr;
- }
-
- /* Allocate a handle out of system memory for the "real" application, and
- verify it is low enough in memory. Always release this handle if allocated.
- If necessary, shrink the allocated handle just above the spot for the real
- app to make room.
- Error if cannot allocate handle for app or resulting handle is too
- high in memory.
- */
- static OSErr VerifyThereIsRoomForApp
- (Handle *bigChunkHA, // array of Handle
- long leaveThisMuch, // for application, or 0, plus...
- short lowestWhichH, // index of lowest bigChunkHA[]
- Size *safetyMarginP // <->...a few 100K, set as we go
- )
- {
- Handle appHandle = NULL; // to preflight app
- Size safetyMargin; // a few 100K, varies per system
- Size amountToAllocate; // available less amount to keep
- OSErr theErr = noErr;
-
- // Get the safety margin.
- safetyMargin = *safetyMarginP;
-
- // -allocate for app, verify it is low.
- appHandle = TempNewHandle(leaveThisMuch, &theErr);
- if (theErr == noErr)
- {
- ; // Handle was allocated for app.
- }
- else if (bigChunkHA[lowestWhichH] != NULL)
- {
- // The handle just above appHandle's spot may be too large
- amountToAllocate = GetHandleSize(bigChunkHA[lowestWhichH]);
- amountToAllocate -= MINIMUM_FREE_INC;
- safetyMargin += MINIMUM_FREE_INC;
- if (amountToAllocate > 0)
- {
- do
- {
- DisposeHandle(bigChunkHA[lowestWhichH]);
- bigChunkHA[lowestWhichH] = TempNewHandle( amountToAllocate,
- &theErr );
- if (theErr == noErr)
- {
- // Attempt to reallocate for the application.
- HLock(bigChunkHA[lowestWhichH]);
- appHandle = TempNewHandle(leaveThisMuch, &theErr);
- }
- else
- {
- // Try to clear the error by allocating less above the app.
- amountToAllocate -= MINIMUM_FREE_INC;
- safetyMargin += MINIMUM_FREE_INC;
- theErr = eCouldNotGetAppChunk_DBMem;
- }
- } while ( theErr != noErr
- && amountToAllocate > 0);
- }
- }
- // Verify the application's handle is low enough.
- if (theErr == noErr)
- {
- if ( (long)*appHandle + leaveThisMuch + MINIMUM_FREE_INC >= APPLICATION_LIMIT )
- {
- theErr = eAppLeftTooHigh_DBMem;
- }
- // else handle is properly low
- }
-
- // -release the spot for the real application
- if (appHandle != NULL)
- DisposeHandle(appHandle);
-
- *safetyMarginP = safetyMargin;
- return theErr;
- }
-
- /* Get size of next chunk of contiguous system memory, deduct leaveThisMuch and
- safety margin, allocate and lock that. safetyMarginP may be adjusted upwards in an
- attempt to clear out-of-memory error.
- Error if try to allocate and TempNewHandle fails on all tries.
- */
- static OSErr AllocateContiguousChunk
- (Size availableContigSysMem, // biggest single piece from sys
- long leaveThisMuch, // for application, or 0, plus...
- Size *safetyMarginP, // <->...a few 100K, set as we go
- Handle *tempMemHP // <- address of Handle
- )
- {
- Size amountToAllocate; // available less amount to leave
- short count = 0; // limit the number of tries
- short topCount = 10; // ditto
- Size safetyMargin;
- OSErr theErr = noErr;
-
- // Get the safety margin.
- safetyMargin = *safetyMarginP;
- // Deduct application size and safety margin from amount free.
- amountToAllocate = availableContigSysMem - leaveThisMuch - safetyMargin;
- // Clear handle just to be safe.
- *tempMemHP = NULL;
-
- // Allocate handle, leaving a bit free for the system to play with.
- if (amountToAllocate > 0)
- {
- do
- {
- *tempMemHP = TempNewHandle(amountToAllocate, &theErr);
- if (theErr != noErr)
- {
- // Try to clear the error (in practice it happens on the last handle)
- amountToAllocate -= MINIMUM_FREE_INC;
- safetyMargin += MINIMUM_FREE_INC;
- }
- } while ( theErr != noErr
- && amountToAllocate > 0
- && ++count < topCount );
- // Lock down the handle if we succeeded.
- if (theErr == noErr)
- {
- HLock(*tempMemHP);
- }
- }
-
- // Report safety margin used and error code.
- *safetyMarginP = safetyMargin;
- return theErr;
- }
-
-
- // Resource-related support
-
- /* Retrieve preferred and minimum sizes for an app.
- Error if cannot open resource fork.
- */
- static OSErr PreferredAndMinimumForApplication
- (FSSpecPtr appSpecP, // ->for the application
- long *preferredSizeP, // <-from SIZE rsrc
- long *minimumSizeP // <-ditto
- )
- {
- short curResFile; // current resource fork ref
- short appResReference; // for the application
- short resErr; // typ. out of memory
- SIZEPtr sizeP; // see FakeStart.h
- Handle h; // for the SIZE rsrc
-
- // Remember current resource file.
- curResFile = CurResFile();
- // Open and use application's resource fork.
- appResReference = FSpOpenResFile(appSpecP, fsRdPerm);
- resErr = ResError();
- if (resErr != noErr || appResReference == -1)
- return fnfErr; // open error
- UseResFile(appResReference);
-
- // Retrieve SIZE 0 or -1 (preferred in that order).
- h = Get1Resource('SIZE', 0);
- if (h == NULL)
- h = Get1Resource('SIZE', -1);
-
- // Set preferred and minimum values.
- if (h != NULL)
- {
- sizeP = (SIZEPtr)*h;
- *preferredSizeP = sizeP->preferredSize;
- *minimumSizeP = sizeP->minimumSize;
- ReleaseResource(h);
- }
- else
- {
- *preferredSizeP = MINIMUM_FOR_APP;
- *minimumSizeP = MINIMUM_FOR_APP;
- }
-
- // Close up, use original resource file.
- CloseResFile(appResReference);
- UseResFile(curResFile);
- // Return sucess.
- return noErr;
- }
-